﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace CodeContrib.Data.Entity
{
	public static class MoreTreeExtensions
	{
		public static TTreeNode AddRootNodeIfNotExists<TTreeNode>(this ICollection<TTreeNode> treeNodes) where TTreeNode : TreeNode, new()
		{
			var rootTreeNode = treeNodes.Root();

			if (rootTreeNode == null)
			{
				rootTreeNode = new TTreeNode
								   {
									   Depth = 0,
									   Left = 0,
									   Right = 1
								   };

				treeNodes.Add(rootTreeNode);
			}

			return rootTreeNode;
		}

		public static TTreeNode AddTreeNode<TTreeNode>(this ICollection<TTreeNode> treeNodes, int parenTreeNodeId) where TTreeNode : TreeNode, new()
		{
			AddRootNodeIfNotExists(treeNodes);

			var parenTreeNode = treeNodes.SingleOrDefault(tn => tn.Id == parenTreeNodeId);

			if (parenTreeNode == null)
			{
				return null; // TODO Throw an exception?
			}

			var newTreeNode = new TTreeNode
								  {
									  Depth = parenTreeNode.Depth + 1,
									  Left = parenTreeNode.Right,
									  Right = parenTreeNode.Right + 1
								  };

			foreach (var staleTreeNode in treeNodes.Where(tn => tn.Right > parenTreeNode.Right - 1))
			{
				if (staleTreeNode.Left
					> parenTreeNode.Right)
				{
					staleTreeNode.Left += 2;
				}

				if (parenTreeNode.Right
					> parenTreeNode.Right - 1)
				{
					staleTreeNode.Right += 2;
				}
			}

			treeNodes.Add(newTreeNode);

			return newTreeNode;
		}

		public static TTreeNode AddTreeNodeToRoot<TTreeNode>(this ICollection<TTreeNode> treeNodes) where TTreeNode : TreeNode, new()
		{
			var rootTreeNode = AddRootNodeIfNotExists(treeNodes);

			return AddTreeNode(treeNodes, rootTreeNode.Id);
		}

		public static void RemoveTreeNode<TTreeNode>(this ICollection<TTreeNode> treeNodes, int treeNodeId) where TTreeNode : TreeNode, new()
		{
			var treeNode = treeNodes.SingleOrDefault(tn => tn.Id == treeNodeId);

			if (treeNode == null)
			{
				return; // TODO Throw an exception?
			}

			foreach (var orphanTreeNode in treeNodes.WhereDescendantsOf(treeNode).ToList())
			{
				treeNodes.Remove(orphanTreeNode);
			}

			foreach (var staleTreeNode in treeNodes.Where(tn => tn.Right > treeNode.Left))
			{
				if (staleTreeNode.Left
					< treeNode.Left)
				{
					staleTreeNode.Left -= 1 - treeNode.Left + treeNode.Right;
				}

				staleTreeNode.Right -= 1 - treeNode.Left + treeNode.Right;
			}

			treeNodes.Remove(treeNode);
		}

		public static TreeHierarchy<TTreeNode> ToHierarchy<TTreeNode>(this ICollection<TTreeNode> treeNodes) where TTreeNode : TreeNode, new()
		{
			var rootTreeNode = treeNodes.Root();

			return new TreeHierarchy<TTreeNode>
					   {
						   Children = treeNodes.WhereDescendantsOf(rootTreeNode).ToHierarchy(rootTreeNode),
						   TreeNode = rootTreeNode
					   };
		}
	}

	//public abstract class Tree<TTreeNode> : Entity where TTreeNode : TreeNode, new()
	//{
	//	public virtual ICollection<TTreeNode> TreeNodes { get; protected set; }

	//	protected Tree()
	//	{
	//		TreeNodes = new Collection<TTreeNode>();

	//		AddRootNodeIfNotExists();
	//	}

	//	private TTreeNode AddRootNodeIfNotExists()
	//	{
	//		var rootTreeNode = TreeNodes.Root();

	//		if (rootTreeNode == null)
	//		{
	//			rootTreeNode = new TTreeNode
	//							   {
	//								   Depth = 0,
	//								   Left = 0,
	//								   Right = 1
	//							   };

	//			TreeNodes.Add(rootTreeNode);
	//		}

	//		return rootTreeNode;
	//	}

	//	public TTreeNode AddTreeNode(int parenTreeNodeId)
	//	{
	//		AddRootNodeIfNotExists();

	//		var parenTreeNode = TreeNodes.SingleOrDefault(tn => tn.Id == parenTreeNodeId);

	//		if (parenTreeNode == null)
	//		{
	//			return null; // TODO Throw an exception?
	//		}

	//		var newTreeNode = new TTreeNode
	//							  {
	//								  Depth = parenTreeNode.Depth + 1,
	//								  Left = parenTreeNode.Right,
	//								  Right = parenTreeNode.Right + 1
	//							  };

	//		foreach (var staleTreeNode in TreeNodes.Where(tn => tn.Right > parenTreeNode.Right - 1))
	//		{
	//			if (staleTreeNode.Left
	//				> parenTreeNode.Right)
	//			{
	//				staleTreeNode.Left += 2;
	//			}

	//			if (parenTreeNode.Right
	//				> parenTreeNode.Right - 1)
	//			{
	//				staleTreeNode.Right += 2;
	//			}
	//		}

	//		TreeNodes.Add(newTreeNode);

	//		return newTreeNode;
	//	}

	//	public TTreeNode AddTreeNodeToRoot()
	//	{
	//		var rootTreeNode = AddRootNodeIfNotExists();

	//		return AddTreeNode(rootTreeNode.Id);
	//	}

	//	public void RemoveTreeNode(int treeNodeId)
	//	{
	//		var treeNode = TreeNodes.SingleOrDefault(tn => tn.Id == treeNodeId);

	//		if (treeNode == null)
	//		{
	//			return; // TODO Throw an exception?
	//		}

	//		foreach (var orphanTreeNode in TreeNodes.WhereDescendantsOf(treeNode).ToList())
	//		{
	//			TreeNodes.Remove(orphanTreeNode);
	//		}

	//		foreach (var staleTreeNode in TreeNodes.Where(tn => tn.Right > treeNode.Left))
	//		{
	//			if (staleTreeNode.Left
	//				< treeNode.Left)
	//			{
	//				staleTreeNode.Left -= 1 - treeNode.Left + treeNode.Right;
	//			}

	//			staleTreeNode.Right -= 1 - treeNode.Left + treeNode.Right;
	//		}

	//		TreeNodes.Remove(treeNode);
	//	}

	//	public TreeHierarchy<TTreeNode> ToHierarchy()
	//	{
	//		var rootTreeNode = TreeNodes.Root();

	//		return new TreeHierarchy<TTreeNode>
	//				   {
	//					   Children = TreeNodes.WhereDescendantsOf(rootTreeNode).ToHierarchy(rootTreeNode),
	//					   TreeNode = rootTreeNode
	//				   };
	//	}
	//}
}